/*
 * file_list.c
 *
 *  Created on: 2011-6-16
 *      Author: JustinLee
 */

#include "file_list.h"
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
#include <assert.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
//#include <memory.h>
#include <string.h>

#define FETCH_ADDTIONAL_FILE_INFO 1

#ifndef inline
#define inline
#endif

static inline void list_free(FILE_INFO_LIST* list)
{
	FILE_INFO_LIST* next = NULL;
	while(list)
	{
		next = list->next;
		free(list);
		list = next;
	}
}

static inline void list_set_zero(FILE_INFO_LIST* list)
{
	FILE_INFO_LIST* next = NULL;
	while(list)
	{
		next = list->next;
		memset(list, 0, sizeof(FILE_INFO_LIST));
		list->next = next;
		list = next;
	}
}

static inline FILE_INFO_LIST* update_list_only_file_info(char* path, FILE_INFO_LIST* old_list)
{
	DIR* dir;
	struct dirent* ent;
	struct stat sts;
	struct tm* t_time;
	int i = 0;
	FILE_INFO_LIST* list = NULL;
	FILE_INFO_LIST* list_head = NULL;
	FILE_INFO_LIST* list_cur = NULL;
	FILE_INFO* cur = NULL;
	char t_path[MAX_PATH_LEN] = {0};
	int t_name_ptr = 0;
	char* t_p_name = NULL;

	dir = opendir(path);
	assert(dir);
	if(dir == NULL)
		return NULL;

	if(old_list == NULL)
	{
		list = (FILE_INFO_LIST*)malloc(sizeof(FILE_INFO_LIST));
		list->next = NULL;
	}
	else
	{
		list = old_list;
	}
	list_set_zero(list);//memset(list, 0, sizeof(FILE_INFO_LIST));

	list_head = list;

	t_name_ptr = strlen(path);
	memcpy(t_path, path, t_name_ptr);
	t_p_name = &t_path[t_name_ptr - 1];
	if(*t_p_name != '/' && *t_p_name != '\\')
	{
		t_path[t_name_ptr] = '/';
		++t_name_ptr;
	}
	t_p_name = &t_path[t_name_ptr];
	memcpy(list->path, t_path, t_name_ptr + 1);

	while((ent = readdir(dir)) != NULL)
	{
		if(ent->d_name[0] == '.')
		{
			continue;
		}
		cur = &list->array[i];
		memcpy(t_p_name,ent->d_name,ent->d_namlen);//strcpy(t_p_name,ent->d_name);//
		t_p_name[ent->d_namlen] = 0;
#if FETCH_ADDTIONAL_FILE_INFO
		if(stat(t_path, &sts) == 0)
		{
			if(sts.st_size <= 0)
			{
				continue;
			}
			memcpy(cur->file_name,ent->d_name,ent->d_namlen);
			cur->file_name[ent->d_namlen] = 0;//strcpy(cur->file_name,ent->d_name);//
			cur->file_size =  sts.st_size;
			t_time = localtime(sts.st_mtime > sts.st_ctime ? &sts.st_mtime : &sts.st_ctime);
			cur->modified_time.year = t_time->tm_year + 1900;
			cur->modified_time.month = t_time->tm_mon + 1;
			cur->modified_time.day = t_time->tm_mday;
			cur->modified_time.hour = t_time->tm_hour;
			cur->modified_time.min = t_time->tm_min;
			cur->modified_time.sec = t_time->tm_sec;
		}
		else
		{
			//printf("ERROR %x\n", errno);
			continue;
		}
#endif
		++i;
		if(i >= MAX_FILE_NUM)
		{
			list->count = i;
			//allocate a new node for list
			if(list->next == NULL)
			{
				list_cur = list;
				list = (FILE_INFO_LIST*)malloc(sizeof(FILE_INFO_LIST));
				memset(list, 0, sizeof(FILE_INFO_LIST));
				list_cur->next = list;
			}
			else
			{
				list = list->next;
			}
			strcpy(list->path, list_head->path);
			i = 0;
		}
	}

	list->count = i;

	closedir(dir);

	//cut off rest nodes in list
	if(list->next != NULL)
	{
		list_free(list->next);
		list->next = NULL;
	}

	return list_head;
}

FILE_INFO_LIST* create_file_list(char* path)
{
	return update_list_only_file_info(path, NULL);
}

void update_file_list(FILE_INFO_LIST* list)
{
	char path[MAX_PATH_LEN] = {0};
	if(list == NULL)
	{
		return;
	}
	strcpy(path, list->path);
	update_list_only_file_info(path, list);
}

void close_file_list(FILE_INFO_LIST* list)
{
	if(list)
	{
		list_free(list);
	}
}

#define STR_MOVIE_TYPES ".mpg.avi.wmv.mp4.rmvb.rm.mov.mkv.3gp.mkv.asf.flv.ts.mpeg2.m4v.f4v"
#define STR_AUDIO_TYPES ".mp3.aac.flac.ogg.midi.wav.pcm.ape.amr.wma.cda.f4a.m4a"
#define STR_PICTURE_TYPES ".bmp.jpg.jpeg.gif.png"

static FILE_TYPE file_type_array[F_T_NUM] = {F_T_VIDEO, F_T_AUDIO, F_T_PICTURE, F_T_OTHER};
static char* file_type_class_array[F_T_NUM] = {STR_MOVIE_TYPES, STR_AUDIO_TYPES, STR_PICTURE_TYPES, ""};

FILE_TYPE get_file_type_by_file_name(char* file_name)
{
    int i = 0;
    char* init_name = file_name;
    char* last_name = NULL;
    while (file_name)
    {
        last_name = file_name;
        file_name = strstr(&file_name[1], ".");
    }
    if (last_name == init_name)
    {
        file_name = NULL;
    }
    else
    {
        file_name = last_name;
    }
    if(file_name)
	{
		for(i = 0; i < F_T_NUM; ++i)
		{
			if(strstr(file_type_class_array[i], file_name))
			{
				return file_type_array[i];
			}
		}
	}
	return F_T_OTHER;
}

int create_copy_file(const char *source, const char *target)
{
    struct stat src_stat;
    
    if (lstat(source, &src_stat) == -1)
        return -1;
    
    if (S_ISLNK(src_stat.st_mode)) {
        char lnk[1024];
        int lnk_size;
        if ((lnk_size = readlink(source, lnk, 1023)) == -1)
            return -2;
        lnk[lnk_size] = '\0';
        if (symlink(lnk, target) == -1)
            return -3;
    } else if (S_ISREG(src_stat.st_mode)) {
        int src, dst;
        int rsize;
        char buf[1024];
        if ((src = open(source, O_RDONLY)) == -1) {
            close(dst);
            return -4;
        }
        if ((dst = creat(target, src_stat.st_mode)) == -1)
            return -5;
        while ((rsize = read(src, buf, 1024))) {
            if (rsize == -1 && errno == EINTR)
                continue ;
            if (rsize == -1) {
                close(src);
                close(dst);
                return -6;
            }
            while (write(dst, buf, rsize) == -1)
                if (errno != EINTR) {
                    close(src);
                    close(dst);
                    return -7;
                }
        }
        close(src);
        close(dst);
    } else {
        return -8;
    }
    return 0;
}
